library(beeswarm)
library(naniar)
# install.packages("janitor")
library(janitor)

Attaching package: ‘janitor’

The following objects are masked from ‘package:stats’:

    chisq.test, fisher.test
library(dplyr)
# install.packages("GGally")
library(sets)

Attaching package: ‘sets’

The following object is masked from ‘package:janitor’:

    %>%

The following object is masked from ‘package:naniar’:

    %>%

The following object is masked from ‘package:dplyr’:

    %>%
library(tidyverse)
── Attaching packages ──────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ tibble  3.0.6     ✓ purrr   0.3.4
✓ tidyr   1.1.2     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
── Conflicts ─────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x forcats::%>%()  masks stringr::%>%(), purrr::%>%(), tidyr::%>%(), tibble::%>%(), sets::%>%(), janitor::%>%(), naniar::%>%(), dplyr::%>%()
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
x purrr::map()    masks maps::map()
library(ggplot2)
library(GGally) # for ggpairs
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2

Attaching package: ‘GGally’

The following object is masked from ‘package:sets’:

    %>%
# install.packages("maps")
library(maps)
# devtools::install_github(“UrbanInstitute/urbnmapr”)

Make some utility functions

load_file <- function(file_path){
  read.csv(file_path)
}

Business Understanding

Need to add some things here about covid.

Data Understanding

# Load some of the data 
tx_data <- load_file("./../data/COVID-19_cases_TX.csv")
global_mobility_report <- load_file("./../data/Global_Mobility_Report.csv")
cases_plus_census <- load_file("./../data/COVID-19_cases_plus_census.csv")

What data is availiable?

There is a large amount of data provided to us for this project. We are given three different csv files:

  • COVID-19_cases_plus_census
    • brief desc.
  • COVID-19_cases_TX
    • brief desc.
  • Global_Mobility_Report
    • brief desc.

All of these files contain important features; however, due to the large amount of data given we will focus on a few of the variables (features) that we believe are critically important. First, lets narrow our focus down to the following variables: ____

head(global_mobility_report)

Verify Data Quality

cols_keep <- c("county_fips_code", "confirmed_cases", "deaths", "median_income", "male_pop", "female_pop", "total_pop", "median_age", "worked_at_home")
# print(cols_keep)
subset_census <- cases_plus_census[cols_keep]
head(subset_census)
tx_missing <- tx_data[cols_with_missing(tx_data)]
global_mobility_report_missing <- global_mobility_report[cols_with_missing(global_mobility_report)]
cases_plus_census_missing <- cases_plus_census[cols_with_missing(cases_plus_census)]
cols_keep <- c("date", "retail_and_recreation_percent_change_from_baseline", "grocery_and_pharmacy_percent_change_from_baseline", "parks_percent_change_from_baseline", "transit_stations_percent_change_from_baseline", "workplaces_percent_change_from_baseline", "residential_percent_change_from_baseline")
# print(cols_keep)
subset_mobility <- global_mobility_report[cols_keep]
head(subset_mobility)
# subset_mobility[['date'] <- as.Date(subset_mobility['date']], format='%m/%d/%y')
# head(subset_mobility)
subset_mobility$date <- as.Date(subset_mobility$date, format="%Y-%m-%d")
# subset_mobility["date"] <- apply(subset_mobility["date"], 2, as.Date, format = '%m/%d/%y')
head(subset_mobility)

sapply(subset_mobility, min, na.rm = TRUE)
summarise(min = min(subset_mobility, na.rm = TRUE),
            max = max(subset_mobility, na.rm = TRUE))
print(min(subset_mobility$date, na.rm=TRUE))
print(max(subset_mobility$date, na.rm=TRUE))

Is there missing data?

cols_with_missing <- function(frame){
  colnames(frame)[ apply(frame, 2, anyNA) ]
}
tx_missing <- tx_data[cols_with_missing(tx_data)]
global_mobility_report_missing <- global_mobility_report[cols_with_missing(global_mobility_report)]
cases_plus_census_missing <- cases_plus_census[cols_with_missing(cases_plus_census)]
class(tx_data)
# Track the columns to see if we are removing any 
mobility_cols_orig <- colnames(global_mobility_report)
census_cols_orig <- colnames(cases_plus_census)
tx_county_cols_orig <- colnames(tx_county_info)
tx_statewide_cols_orig <- colnames(tx_statewide_info)

# Remove any columns that do not contain any data
global_mobility_report <- remove_empty(global_mobility_report)
value for "which" not specified, defaulting to c("rows", "cols")
cases_plus_census <- remove_empty(cases_plus_census)
value for "which" not specified, defaulting to c("rows", "cols")
tx_county_info <- remove_empty(tx_county_info)
value for "which" not specified, defaulting to c("rows", "cols")
tx_statewide_info <- remove_empty(tx_statewide_info)
value for "which" not specified, defaulting to c("rows", "cols")
# Remove columns that just have a constant value 
global_mobility_report <- remove_constant(global_mobility_report)
cases_plus_census <- remove_constant(cases_plus_census)
tx_county_info <- remove_constant(tx_county_info)
tx_statewide_info <- remove_constant(tx_statewide_info)

mobility_cols_new <- colnames(global_mobility_report)
census_cols_new <- colnames(cases_plus_census)
tx_county_cols_new <- colnames(tx_county_info)
tx_statewide_cols_new <- colnames(tx_statewide_info)

Clean up DataFrames

First, we noticed that much of our data within the COVID-19_cases_TX file had many rows of data that are statewide and not just for a specific county. Let’s modify the data frame to be texas_county_info and texas_statewide_info.

# Get sets for columns 
print(as.set(mobility_cols_orig) - as.set(mobility_cols_new))
{}
print(as.set(census_cols_orig) - as.set(census_cols_new))
{"date", "do_date", "pop_15_and_over", "pop_5_years_over", "pop_divorced", "pop_never_married",
 "pop_now_married", "pop_separated", "pop_widowed", "speak_only_english_at_home",
 "speak_spanish_at_home", "speak_spanish_at_home_low_english"}
print(as.set(tx_county_cols_orig) - as.set(tx_county_cols_new))
{"state", "state_fips_code"}
print(as.set(tx_county_cols_orig) - as.set(tx_statewide_cols_new))
{"county_fips_code", "county_name", "state", "state_fips_code"}
head(tx_county_info)
head(tx_statewide_info)

Remove Columns from Data Frames

Remove columns from the dataframes that do not have any data, have the same data repeated (not useful since implicit knowledge)

# Track the columns to see if we are removing any 
mobility_cols_orig <- colnames(global_mobility_report)
census_cols_orig <- colnames(cases_plus_census)
tx_county_cols_orig <- colnames(tx_county_info)
tx_statewide_cols_orig <- colnames(tx_statewide_info)

# Remove any columns that do not contain any data
global_mobility_report <- remove_empty(global_mobility_report)
cases_plus_census <- remove_empty(cases_plus_census)
tx_county_info <- remove_empty(tx_county_info)
tx_statewide_info <- remove_empty(tx_statewide_info)

# Remove columns that just have a constant value 
global_mobility_report <- remove_constant(global_mobility_report)
cases_plus_census <- remove_constant(cases_plus_census)
tx_county_info <- remove_constant(tx_county_info)
tx_statewide_info <- remove_constant(tx_statewide_info)

mobility_cols_new <- colnames(global_mobility_report)
census_cols_new <- colnames(cases_plus_census)
tx_county_cols_new <- colnames(tx_county_info)
tx_statewide_cols_new <- colnames(tx_statewide_info)
# Get sets for columns 
print(as.set(mobility_cols_orig) - as.set(mobility_cols_new))
print(as.set(census_cols_orig) - as.set(census_cols_new))
print(as.set(tx_county_cols_orig) - as.set(tx_county_cols_new))
print(as.set(tx_county_cols_orig) - as.set(tx_statewide_cols_new))

When removing columns that either do not have data or a constant is repeated throughout the column, the global_mobility_report remains the same; however, the following columns are dropped for the respective dataframes:

  • census
    • date, do_date, pop_15_and_over, pop_5_years_over, pop_divorced, pop_never_married, pop_now_married, pop_separated, pop_widowed, speak_only_english_at_home, speak_spanish_at_home,and speak_spanish_at_home_low_english
  • tx_county_info
    • state, state_fips_code
  • tx_statewide_info
    • county_fips_code, county_name, state, and state_fips_code
head(tx_county_info)

Are there Duplicates?

mobility_duplicates <- duplicated(global_mobility_report)
census_duplicates <- duplicated(cases_plus_census)
tx_county_duplicates <- duplicated(tx_county_info)
tx_statewide_duplicates <- duplicated(tx_statewide_info)

print(sum(mobility_duplicates, na.rm = TRUE))
print(sum(census_duplicates, na.rm = TRUE))
print(sum(tx_county_duplicates, na.rm = TRUE))
print(sum(tx_statewide_duplicates, na.rm = TRUE))

We can see that there were no duplicate observations in the data. At least none on the basis that the data was the exact same.

unique(mobility_duplicates)

Create columns based on population

# make a pct infected column (virus so should get 1 time, but this is disputed for covid)
subset_census['pct_infected'] <- subset_census['confirmed_cases']/subset_census['total_pop']
subset_census['pct_deaths'] <- subset_census['deaths']/subset_census['total_pop']
head(subset_census)
library(RColorBrewer)
plot_vs_county <- function(df, col_val, percentile=FALSE,
                           fips_title="county_fips_code", banks=6, 
                           legend_title="", graphic_title=""){
  # Subset for speed 
  df <- df[c(fips_title, col_val)]
  
  # Get county data
  gcounty <- ggplot2::map_data("county")
  # USA map data
  gusa <- map_data("state")
  
  if (banks > 9){
    mycolors <- colorRampPalette(brewer.pal(9, "Reds"))(banks)
  }
  
  # Format with subregions
  fipstab <-
      transmute(maps::county.fips, fips, county = sub(":.*", "", polyname)) %>%
      unique() %>%
      separate(county, c("region", "subregion"), sep = ",")
  
  # Combine in desired order (NA for missing)
  gcounty <- left_join(gcounty, fipstab, c("region", "subregion"))


  dis <- df
  dis$rprop <- rank(df[col_val])
  dis$pcls <- cut(100 * percent_rank(df[col_val]), seq(0, 100, len = banks),
                        include.lowest = TRUE)

  # Missing data
  anti_join(gcounty, dis, by = c("fips" = fips_title)) %>%
    select(region, subregion) %>%
    unique()
  gcounty_pop <- left_join(gcounty, dis, by = c("fips" = fips_title))
  fill_vals <- gcounty_pop[col_val]

  # Plot
  if (legend_title == ""){
    legend_title <- col_val
  }

  if (percentile == FALSE){
    # names(gcounty_pop)[names(gcounty_pop) == col_val] <- "col_of_interest"
    plt <- ggplot(gcounty_pop) +
      geom_polygon(aes(long, lat, group = group, fill = get(col_val)),
                   color = "grey", size = 0.1, name="Percent Infected") +
      geom_polygon(aes(long, lat, group = group),
                   fill = NA, data = gusa, color = "lightgrey") +
      coord_map("bonne", parameters = 41.6) + ggthemes::theme_map()+
      scale_fill_gradient2()
       # scale_fill_gradient(low = "white", high = "red", na.value = "grey")
      # scale_fill_gradientn(colours = terrain.colors(10))
  }

  if (percentile == TRUE){
    plt <- ggplot(gcounty_pop) +
      geom_polygon(aes(long, lat, group = group, fill = pcls),
                   color = "grey", size = 0.1) +
      geom_polygon(aes(long, lat, group = group),
                   fill = NA, data = gusa, color = "lightgrey") +
      coord_map("bonne", parameters = 41.6) + ggthemes::theme_map() +
      scale_fill_manual(values = mycolors, na.value = "grey") +
      # scale_fill_brewer(palette = "viridis", na.value = "grey") +
      theme(plot.title = element_text(family = "Helvetica", face = "bold", size = (15)),
            legend.background = element_rect(fill = NA), 
            legend.position = "left")
  }
  plt <- plt + labs(fill=legend_title) + ggtitle(graphic_title)
  plt
}
plot_vs_county(subset_census, "pct_infected", legend_title = "Percent Infected")
Ignoring unknown parameters: name

plot_vs_county(subset_census, "pct_infected", percentile = TRUE, banks=11, 
               legend_title = "Percentile Infected",
               graphic_title = "Percentile of Percentage of People Infected by County")

library(maps)

census_infected <- subset_census[c("county_fips_code", "pct_infected")]
# census_infected$color_density <- rainbow(n = 50, census_infected$pct_infected / max(census_infected$pct_infected))
census_infected$color_density <- heat.colors(census_infected$pct_infected/max(census_infected$pct_infected))
Error in `$<-.data.frame`(`*tmp*`, color_density, value = character(0)) : 
  replacement has 0 rows, data has 3142
data(county.fips)
## Set up fake df_pop_county data frame
df_pop_county <- data.frame(region=county.fips$fips)
df_pop_county$value <- county.fips$fips
y <- df_pop_county$value
df_pop_county$color <- gray(y / max(y))

## merge population data with county.fips to make sure color column is
## ordered correctly.
counties <- county.fips %>% left_join(df_pop_county, by=c('fips'='region'))
# print(head(counties))

newdata <- census_infected[order(census_infected$county_fips_code),]
newdata

# Through away counties that are not in default
new_data <- newdata[(newdata$county_fips_code %in% counties$fips),]
new_data
data(county.fips)
## Set up fake df_pop_county data frame
df_pop_county <- data.frame(region=county.fips$fips)
df_pop_county$value <- county.fips$fips
y <- df_pop_county$value
df_pop_county$color <- gray(y / max(y))

## merge population data with county.fips to make sure color column is
## ordered correctly.
counties <- county.fips %>% left_join(df_pop_county, by=c('fips'='region'))
Error in county.fips %>% left_join(df_pop_county, by = c(fips = "region")) : 
  could not find function "%>%"
census_infected
data(county.fips)
df_pop_county <- data.frame(region=county.fips$fips)
df_pop_county
plot_vs_county <- function(df, col_val, percentile=FALSE, fips_title="county_fips_code", banks=6, legend_title=""){
  # Subset for speed 
  df <- df[c(fips_title, col_val)]
  
  # Get county data
  gcounty <- ggplot2::map_data("county")
  # USA map data
  gusa <- map_data("state")
  
  # Format with subregions
  fipstab <-
      transmute(maps::county.fips, fips, county = sub(":.*", "", polyname)) %>%
      unique() %>%
      separate(county, c("region", "subregion"), sep = ",")
  
  # Combine in desired order (NA for missing)
  gcounty <- left_join(gcounty, fipstab, c("region", "subregion"))


  dis <- df
  dis$rprop <- rank(df[col_val])
  dis$pcls <- cut(100 * percent_rank(df[col_val]), seq(0, 100, len = banks),
                        include.lowest = TRUE)

  # Missing data
  anti_join(gcounty, dis, by = c("fips" = fips_title)) %>%
    select(region, subregion) %>%
    unique()
  gcounty_pop <- left_join(gcounty, dis, by = c("fips" = fips_title))
  fill_vals <- gcounty_pop[col_val]

  # Plot
  if (legend_title == ""){
    legend_title <- col_val
  }

  if (percentile == FALSE){
    # names(gcounty_pop)[names(gcounty_pop) == col_val] <- "col_of_interest"
    plt <- ggplot(gcounty_pop) +
      geom_polygon(aes(long, lat, group = group, fill = get(col_val)),
                   color = "grey", size = 0.1, name="Percent Infected") +
      geom_polygon(aes(long, lat, group = group),
                   fill = NA, data = gusa, color = "lightgrey") +
      coord_map("bonne", parameters = 41.6) + ggthemes::theme_map()
  }

  if (percentile == TRUE){
    plt <- ggplot(gcounty_pop) +
      geom_polygon(aes(long, lat, group = group, fill = pcls),
                   color = "grey", size = 0.1) +
      geom_polygon(aes(long, lat, group = group),
                   fill = NA, data = gusa, color = "lightgrey") +
      coord_map("bonne", parameters = 41.6) + ggthemes::theme_map() +
      scale_fill_brewer(palette = "Reds", na.value = "blue") +
      theme(legend.background = element_rect(fill = NA))
  }
  plt <- plt + labs(fill=legend_title)
  plt
}
ggplot2::map_data("county")
as_tibble(maps::county.fips)
# dall <- ggplot2::map_data("county") %>% left_join(as_tibble(maps::county.fips))
install.packages("mapproj")
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.0/mapproj_1.2.7.tgz'
Content type 'application/x-gzip' length 83240 bytes (81 KB)
==================================================
downloaded 81 KB

The downloaded binary packages are in
    /var/folders/2t/zk2m3vcj2_51x1r2p3cbnt9r0000gn/T//RtmpficDEN/downloaded_packages
# http://homepage.stat.uiowa.edu/~luke/classes/STAT4580-2020/maps.html
# Choropleth Maps of County Population
library(ggplot2)
# install.packages(mapproj)
library(mapproj)

gcounty <- ggplot2::map_data("county")
ggplot(gcounty) +
    geom_polygon(aes(long, lat, group = group),
                 fill = NA, color = "black", size = 0.05) +
    coord_map("bonne", parameters = 41.6)

head(filter(maps::county.fips, grepl(":", polyname)))
fipstab <-
    transmute(maps::county.fips, fips, county = sub(":.*", "", polyname)) %>%
    unique() %>%
    separate(county, c("region", "subregion"), sep = ",")
head(fipstab)
gcounty <- left_join(gcounty, fipstab, c("region", "subregion"))
head(gcounty)
test <- gcounty %>% left_join(census_infected, by=c('fips'='county_fips_code'))
test
install.packages("ggthemes")
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.0/ggthemes_4.2.4.tgz'
Content type 'application/x-gzip' length 436743 bytes (426 KB)
==================================================
downloaded 426 KB

The downloaded binary packages are in
    /var/folders/2t/zk2m3vcj2_51x1r2p3cbnt9r0000gn/T//RtmpficDEN/downloaded_packages
library(ggthemes)
census_infected
census_infected %>% rank(pct_infected)
Error in rank(., pct_infected) : object 'pct_infected' not found
ncls <- 6
w <- census_infected %>% select(fips = county_fips_code, pct_infected) %>% mutate(rpop = rank(pct_infected),
           pcls = cut(100 * percent_rank(pct_infected), seq(0, 100, len = ncls),
                      include.lowest = TRUE))
w
anti_join(gcounty, w, "fips") %>%
    select(region, subregion) %>%
    unique()

gcounty_pop <- left_join(gcounty, w, "fips")
# filter(gcounty_pop, is.na(rpop)) %>%
#     select(region, subregion, pop, rpop, pcls) %>%
#     unique()
gcounty_pop
gusa <- map_data("state")
ggplot(gcounty_pop) +
    geom_polygon(aes(long, lat, group = group, fill = pct_infected),
                 color = "grey", size = 0.1, name="Percent Infected") +
    geom_polygon(aes(long, lat, group = group),
                 fill = NA, data = gusa, color = "lightgrey") +
    coord_map("bonne", parameters = 41.6) + ggthemes::theme_map()
Ignoring unknown parameters: name

ggplot(gcounty_pop) +
    geom_polygon(aes(long, lat, group = group, fill = pcls),
                 color = "grey", size = 0.1) +
    geom_polygon(aes(long, lat, group = group),
                 fill = NA, data = gusa, color = "lightgrey") +
    coord_map("bonne", parameters = 41.6) + ggthemes::theme_map() +
    scale_fill_brewer(palette = "Reds", na.value = "blue",
                      name = "Percentile") +
    theme(legend.background = element_rect(fill = NA))

plot_vs_county <- function(df, col_val, percentile=FALSE, fips_title="county_fips_code", banks=6, legend_title=""){
  # Subset for speed 
  df <- df[c(fips_title, col_val)]
  
  # Get county data
  gcounty <- ggplot2::map_data("county")
  # USA map data
  gusa <- map_data("state")
  
  # Format with subregions
  fipstab <-
      transmute(maps::county.fips, fips, county = sub(":.*", "", polyname)) %>%
      unique() %>%
      separate(county, c("region", "subregion"), sep = ",")
  
  # Combine in desired order (NA for missing)
  gcounty <- left_join(gcounty, fipstab, c("region", "subregion"))


  dis <- df
  dis$rprop <- rank(df[col_val])
  dis$pcls <- cut(100 * percent_rank(df[col_val]), seq(0, 100, len = banks),
                        include.lowest = TRUE)

  # Missing data
  anti_join(gcounty, dis, by = c("fips" = fips_title)) %>%
    select(region, subregion) %>%
    unique()
  gcounty_pop <- left_join(gcounty, dis, by = c("fips" = fips_title))
  fill_vals <- gcounty_pop[col_val]

  # Plot
  if (legend_title == ""){
    legend_title <- col_val
  }

  if (percentile == FALSE){
    # names(gcounty_pop)[names(gcounty_pop) == col_val] <- "col_of_interest"
    plt <- ggplot(gcounty_pop) +
      geom_polygon(aes(long, lat, group = group, fill = get(col_val)),
                   color = "grey", size = 0.1, name="Percent Infected") +
      geom_polygon(aes(long, lat, group = group),
                   fill = NA, data = gusa, color = "lightgrey") +
      coord_map("bonne", parameters = 41.6) + ggthemes::theme_map()
  }

  if (percentile == TRUE){
    plt <- ggplot(gcounty_pop) +
      geom_polygon(aes(long, lat, group = group, fill = pcls),
                   color = "grey", size = 0.1) +
      geom_polygon(aes(long, lat, group = group),
                   fill = NA, data = gusa, color = "lightgrey") +
      coord_map("bonne", parameters = 41.6) + ggthemes::theme_map() +
      scale_fill_brewer(palette = "Reds", na.value = "blue") +
      theme(legend.background = element_rect(fill = NA))
  }
  plt <- plt + labs(fill=legend_title)
  plt
}
subset_census
# plot_vs_county(subset_census, "pct_infected", legend_title = "Pecent Infected")
plot_vs_county(subset_census, "pct_infected", percentile = TRUE, legend_title = "Percentile Infected", banks = 10)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkoYmVlc3dhcm0pCmxpYnJhcnkobmFuaWFyKQojIGluc3RhbGwucGFja2FnZXMoImphbml0b3IiKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoZHBseXIpCiMgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikKbGlicmFyeShzZXRzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEdHYWxseSkgIyBmb3IgZ2dwYWlycwojIGluc3RhbGwucGFja2FnZXMoIm1hcHMiKQpsaWJyYXJ5KG1hcHMpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKOKAnFVyYmFuSW5zdGl0dXRlL3VyYm5tYXBy4oCdKQpgYGAKCgojIE1ha2Ugc29tZSB1dGlsaXR5IGZ1bmN0aW9ucwoKYGBge3J9CmxvYWRfZmlsZSA8LSBmdW5jdGlvbihmaWxlX3BhdGgpewogIHJlYWQuY3N2KGZpbGVfcGF0aCkKfQoKCgpgYGAKCgojIEJ1c2luZXNzIFVuZGVyc3RhbmRpbmcKCk5lZWQgdG8gYWRkIHNvbWUgdGhpbmdzIGhlcmUgYWJvdXQgY292aWQuCgojIERhdGEgVW5kZXJzdGFuZGluZwoKYGBge3J9CiMgTG9hZCBzb21lIG9mIHRoZSBkYXRhIAp0eF9kYXRhIDwtIGxvYWRfZmlsZSgiLi8uLi9kYXRhL0NPVklELTE5X2Nhc2VzX1RYLmNzdiIpCmdsb2JhbF9tb2JpbGl0eV9yZXBvcnQgPC0gbG9hZF9maWxlKCIuLy4uL2RhdGEvR2xvYmFsX01vYmlsaXR5X1JlcG9ydC5jc3YiKQpjYXNlc19wbHVzX2NlbnN1cyA8LSBsb2FkX2ZpbGUoIi4vLi4vZGF0YS9DT1ZJRC0xOV9jYXNlc19wbHVzX2NlbnN1cy5jc3YiKQpgYGAKCiMjIFdoYXQgZGF0YSBpcyBhdmFpbGlhYmxlPwpUaGVyZSBpcyBhIGxhcmdlIGFtb3VudCBvZiBkYXRhIHByb3ZpZGVkIHRvIHVzIGZvciB0aGlzIHByb2plY3QuICBXZSBhcmUgZ2l2ZW4gdGhyZWUgZGlmZmVyZW50IGNzdiBmaWxlczoKCiogQ09WSUQtMTlfY2FzZXNfcGx1c19jZW5zdXMKICAqIGJyaWVmIGRlc2MuCiogQ09WSUQtMTlfY2FzZXNfVFgKICAqIGJyaWVmIGRlc2MuCiogR2xvYmFsX01vYmlsaXR5X1JlcG9ydAogICogYnJpZWYgZGVzYy4KCkFsbCBvZiB0aGVzZSBmaWxlcyBjb250YWluIGltcG9ydGFudCBmZWF0dXJlczsgaG93ZXZlciwgZHVlIHRvIHRoZSBsYXJnZSBhbW91bnQgb2YgZGF0YSBnaXZlbiB3ZSB3aWxsIGZvY3VzIG9uIGEgZmV3IG9mIHRoZSB2YXJpYWJsZXMgKGZlYXR1cmVzKSB0aGF0IHdlIGJlbGlldmUgYXJlIGNyaXRpY2FsbHkgaW1wb3J0YW50LiAgRmlyc3QsIGxldHMgbmFycm93IG91ciBmb2N1cyBkb3duIHRvIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOiBfX19fCmBgYHtyfQpoZWFkKGdsb2JhbF9tb2JpbGl0eV9yZXBvcnQpCmBgYAoKCiMjIFZlcmlmeSBEYXRhIFF1YWxpdHkKYGBge3J9CmNvbHNfa2VlcCA8LSBjKCJjb3VudHlfZmlwc19jb2RlIiwgImNvbmZpcm1lZF9jYXNlcyIsICJkZWF0aHMiLCAibWVkaWFuX2luY29tZSIsICJtYWxlX3BvcCIsICJmZW1hbGVfcG9wIiwgInRvdGFsX3BvcCIsICJtZWRpYW5fYWdlIiwgIndvcmtlZF9hdF9ob21lIikKIyBwcmludChjb2xzX2tlZXApCnN1YnNldF9jZW5zdXMgPC0gY2FzZXNfcGx1c19jZW5zdXNbY29sc19rZWVwXQpoZWFkKHN1YnNldF9jZW5zdXMpCmBgYApgYGB7cn0Kc2FwcGx5KHN1YnNldF9jZW5zdXMsIG1pbiwgbmEucm0gPSBUUlVFKQpgYGAKYGBge3J9CmNvbHNfa2VlcCA8LSBjKCJkYXRlIiwgInJldGFpbF9hbmRfcmVjcmVhdGlvbl9wZXJjZW50X2NoYW5nZV9mcm9tX2Jhc2VsaW5lIiwgImdyb2NlcnlfYW5kX3BoYXJtYWN5X3BlcmNlbnRfY2hhbmdlX2Zyb21fYmFzZWxpbmUiLCAicGFya3NfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZSIsICJ0cmFuc2l0X3N0YXRpb25zX3BlcmNlbnRfY2hhbmdlX2Zyb21fYmFzZWxpbmUiLCAid29ya3BsYWNlc19wZXJjZW50X2NoYW5nZV9mcm9tX2Jhc2VsaW5lIiwgInJlc2lkZW50aWFsX3BlcmNlbnRfY2hhbmdlX2Zyb21fYmFzZWxpbmUiKQojIHByaW50KGNvbHNfa2VlcCkKc3Vic2V0X21vYmlsaXR5IDwtIGdsb2JhbF9tb2JpbGl0eV9yZXBvcnRbY29sc19rZWVwXQpgYGAKCgpgYGB7cn0KaGVhZChzdWJzZXRfbW9iaWxpdHkpCmBgYApgYGB7cn0KIyBzdWJzZXRfbW9iaWxpdHlbWydkYXRlJ10gPC0gYXMuRGF0ZShzdWJzZXRfbW9iaWxpdHlbJ2RhdGUnXV0sIGZvcm1hdD0nJW0vJWQvJXknKQojIGhlYWQoc3Vic2V0X21vYmlsaXR5KQpzdWJzZXRfbW9iaWxpdHkkZGF0ZSA8LSBhcy5EYXRlKHN1YnNldF9tb2JpbGl0eSRkYXRlLCBmb3JtYXQ9IiVZLSVtLSVkIikKYGBgCgoKCmBgYHtyfQojIHN1YnNldF9tb2JpbGl0eVsiZGF0ZSJdIDwtIGFwcGx5KHN1YnNldF9tb2JpbGl0eVsiZGF0ZSJdLCAyLCBhcy5EYXRlLCBmb3JtYXQgPSAnJW0vJWQvJXknKQpoZWFkKHN1YnNldF9tb2JpbGl0eSkKCnNhcHBseShzdWJzZXRfbW9iaWxpdHksIG1pbiwgbmEucm0gPSBUUlVFKQpgYGAKYGBge3J9CnN1bW1hcmlzZShtaW4gPSBtaW4oc3Vic2V0X21vYmlsaXR5LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBtYXggPSBtYXgoc3Vic2V0X21vYmlsaXR5LCBuYS5ybSA9IFRSVUUpKQpgYGAKYGBge3J9CnByaW50KG1pbihzdWJzZXRfbW9iaWxpdHkkZGF0ZSwgbmEucm09VFJVRSkpCnByaW50KG1heChzdWJzZXRfbW9iaWxpdHkkZGF0ZSwgbmEucm09VFJVRSkpCmBgYAoKCiMjIyBJcyB0aGVyZSBtaXNzaW5nIGRhdGE/CmBgYHtyfQpjb2xzX3dpdGhfbWlzc2luZyA8LSBmdW5jdGlvbihmcmFtZSl7CiAgY29sbmFtZXMoZnJhbWUpWyBhcHBseShmcmFtZSwgMiwgYW55TkEpIF0KfQpgYGAKCgpgYGB7cn0KdHhfbWlzc2luZyA8LSB0eF9kYXRhW2NvbHNfd2l0aF9taXNzaW5nKHR4X2RhdGEpXQpnbG9iYWxfbW9iaWxpdHlfcmVwb3J0X21pc3NpbmcgPC0gZ2xvYmFsX21vYmlsaXR5X3JlcG9ydFtjb2xzX3dpdGhfbWlzc2luZyhnbG9iYWxfbW9iaWxpdHlfcmVwb3J0KV0KY2FzZXNfcGx1c19jZW5zdXNfbWlzc2luZyA8LSBjYXNlc19wbHVzX2NlbnN1c1tjb2xzX3dpdGhfbWlzc2luZyhjYXNlc19wbHVzX2NlbnN1cyldCmBgYAoKYGBge3J9CmNsYXNzKHR4X2RhdGEpCmBgYAoKCgpgYGB7cn0KIyB2aXNfbWlzcyh0eF9taXNzaW5nLCBzb3J0X21pc3MgPSBUKQojIHZpc19taXNzKGdsb2JhbF9tb2JpbGl0eV9yZXBvcnQsIHdhcm5fbGFyZ2VfZGF0YSA9IEYpCnZpc19taXNzKGNhc2VzX3BsdXNfY2Vuc3VzX21pc3NpbmcsIHNvcnRfbWlzcyA9IFQpCgpgYGAKCiMjIENsZWFuIHVwIERhdGFGcmFtZXMKRmlyc3QsIHdlIG5vdGljZWQgdGhhdCBtdWNoIG9mIG91ciBkYXRhIHdpdGhpbiB0aGUgQ09WSUQtMTlfY2FzZXNfVFggZmlsZSBoYWQgbWFueSByb3dzIG9mIGRhdGEgdGhhdCBhcmUgc3RhdGV3aWRlIGFuZCBub3QganVzdCBmb3IgYSBzcGVjaWZpYyBjb3VudHkuICBMZXQncyBtb2RpZnkgdGhlIGRhdGEgZnJhbWUgdG8gYmUgdGV4YXNfY291bnR5X2luZm8gYW5kIHRleGFzX3N0YXRld2lkZV9pbmZvLgoKYGBge3J9CnR4X2NvdW50eV9pbmZvID0gZmlsdGVyKHR4X2RhdGEsIGNvdW50eV9uYW1lICE9ICJTdGF0ZXdpZGUgVW5hbGxvY2F0ZWQiKQp0eF9zdGF0ZXdpZGVfaW5mbyA9IGZpbHRlcih0eF9kYXRhLCBjb3VudHlfbmFtZSA9PSAiU3RhdGV3aWRlIFVuYWxsb2NhdGVkIikKYGBgCgoKYGBge3J9CmhlYWQodHhfY291bnR5X2luZm8pCmBgYAoKCmBgYHtyfQpoZWFkKHR4X3N0YXRld2lkZV9pbmZvKQpgYGAKCiMjIyBSZW1vdmUgQ29sdW1ucyBmcm9tIERhdGEgRnJhbWVzCgpSZW1vdmUgY29sdW1ucyBmcm9tIHRoZSBkYXRhZnJhbWVzIHRoYXQgZG8gbm90IGhhdmUgYW55IGRhdGEsIGhhdmUgdGhlIHNhbWUgZGF0YSByZXBlYXRlZCAobm90IHVzZWZ1bCBzaW5jZSBpbXBsaWNpdCBrbm93bGVkZ2UpCgpgYGB7cn0KIyBUcmFjayB0aGUgY29sdW1ucyB0byBzZWUgaWYgd2UgYXJlIHJlbW92aW5nIGFueSAKbW9iaWxpdHlfY29sc19vcmlnIDwtIGNvbG5hbWVzKGdsb2JhbF9tb2JpbGl0eV9yZXBvcnQpCmNlbnN1c19jb2xzX29yaWcgPC0gY29sbmFtZXMoY2FzZXNfcGx1c19jZW5zdXMpCnR4X2NvdW50eV9jb2xzX29yaWcgPC0gY29sbmFtZXModHhfY291bnR5X2luZm8pCnR4X3N0YXRld2lkZV9jb2xzX29yaWcgPC0gY29sbmFtZXModHhfc3RhdGV3aWRlX2luZm8pCgojIFJlbW92ZSBhbnkgY29sdW1ucyB0aGF0IGRvIG5vdCBjb250YWluIGFueSBkYXRhCmdsb2JhbF9tb2JpbGl0eV9yZXBvcnQgPC0gcmVtb3ZlX2VtcHR5KGdsb2JhbF9tb2JpbGl0eV9yZXBvcnQpCmNhc2VzX3BsdXNfY2Vuc3VzIDwtIHJlbW92ZV9lbXB0eShjYXNlc19wbHVzX2NlbnN1cykKdHhfY291bnR5X2luZm8gPC0gcmVtb3ZlX2VtcHR5KHR4X2NvdW50eV9pbmZvKQp0eF9zdGF0ZXdpZGVfaW5mbyA8LSByZW1vdmVfZW1wdHkodHhfc3RhdGV3aWRlX2luZm8pCgojIFJlbW92ZSBjb2x1bW5zIHRoYXQganVzdCBoYXZlIGEgY29uc3RhbnQgdmFsdWUgCmdsb2JhbF9tb2JpbGl0eV9yZXBvcnQgPC0gcmVtb3ZlX2NvbnN0YW50KGdsb2JhbF9tb2JpbGl0eV9yZXBvcnQpCmNhc2VzX3BsdXNfY2Vuc3VzIDwtIHJlbW92ZV9jb25zdGFudChjYXNlc19wbHVzX2NlbnN1cykKdHhfY291bnR5X2luZm8gPC0gcmVtb3ZlX2NvbnN0YW50KHR4X2NvdW50eV9pbmZvKQp0eF9zdGF0ZXdpZGVfaW5mbyA8LSByZW1vdmVfY29uc3RhbnQodHhfc3RhdGV3aWRlX2luZm8pCgptb2JpbGl0eV9jb2xzX25ldyA8LSBjb2xuYW1lcyhnbG9iYWxfbW9iaWxpdHlfcmVwb3J0KQpjZW5zdXNfY29sc19uZXcgPC0gY29sbmFtZXMoY2FzZXNfcGx1c19jZW5zdXMpCnR4X2NvdW50eV9jb2xzX25ldyA8LSBjb2xuYW1lcyh0eF9jb3VudHlfaW5mbykKdHhfc3RhdGV3aWRlX2NvbHNfbmV3IDwtIGNvbG5hbWVzKHR4X3N0YXRld2lkZV9pbmZvKQoKYGBgCgpgYGB7cn0KIyBHZXQgc2V0cyBmb3IgY29sdW1ucyAKcHJpbnQoYXMuc2V0KG1vYmlsaXR5X2NvbHNfb3JpZykgLSBhcy5zZXQobW9iaWxpdHlfY29sc19uZXcpKQpwcmludChhcy5zZXQoY2Vuc3VzX2NvbHNfb3JpZykgLSBhcy5zZXQoY2Vuc3VzX2NvbHNfbmV3KSkKcHJpbnQoYXMuc2V0KHR4X2NvdW50eV9jb2xzX29yaWcpIC0gYXMuc2V0KHR4X2NvdW50eV9jb2xzX25ldykpCnByaW50KGFzLnNldCh0eF9jb3VudHlfY29sc19vcmlnKSAtIGFzLnNldCh0eF9zdGF0ZXdpZGVfY29sc19uZXcpKQpgYGAKCldoZW4gcmVtb3ZpbmcgY29sdW1ucyB0aGF0IGVpdGhlciBkbyBub3QgaGF2ZSBkYXRhIG9yIGEgY29uc3RhbnQgaXMgcmVwZWF0ZWQgdGhyb3VnaG91dCB0aGUgY29sdW1uLCB0aGUgZ2xvYmFsX21vYmlsaXR5X3JlcG9ydCByZW1haW5zIHRoZSBzYW1lOyBob3dldmVyLCB0aGUgZm9sbG93aW5nIGNvbHVtbnMgYXJlIGRyb3BwZWQgZm9yIHRoZSByZXNwZWN0aXZlIGRhdGFmcmFtZXM6CgoqIGNlbnN1cwogICogZGF0ZSwgZG9fZGF0ZSwgcG9wXzE1X2FuZF9vdmVyLCBwb3BfNV95ZWFyc19vdmVyLCBwb3BfZGl2b3JjZWQsIHBvcF9uZXZlcl9tYXJyaWVkLCBwb3Bfbm93X21hcnJpZWQsIHBvcF9zZXBhcmF0ZWQsIHBvcF93aWRvd2VkLCBzcGVha19vbmx5X2VuZ2xpc2hfYXRfaG9tZSwgc3BlYWtfc3BhbmlzaF9hdF9ob21lLGFuZCBzcGVha19zcGFuaXNoX2F0X2hvbWVfbG93X2VuZ2xpc2gKKiB0eF9jb3VudHlfaW5mbwogICogc3RhdGUsIHN0YXRlX2ZpcHNfY29kZQoqIHR4X3N0YXRld2lkZV9pbmZvCiAgKiBjb3VudHlfZmlwc19jb2RlLCBjb3VudHlfbmFtZSwgc3RhdGUsIGFuZCBzdGF0ZV9maXBzX2NvZGUKICAKYGBge3J9CmhlYWQodHhfY291bnR5X2luZm8pCmBgYAoKIyMjIEFyZSB0aGVyZSBEdXBsaWNhdGVzPwpgYGB7cn0KbW9iaWxpdHlfZHVwbGljYXRlcyA8LSBkdXBsaWNhdGVkKGdsb2JhbF9tb2JpbGl0eV9yZXBvcnQpCmNlbnN1c19kdXBsaWNhdGVzIDwtIGR1cGxpY2F0ZWQoY2FzZXNfcGx1c19jZW5zdXMpCnR4X2NvdW50eV9kdXBsaWNhdGVzIDwtIGR1cGxpY2F0ZWQodHhfY291bnR5X2luZm8pCnR4X3N0YXRld2lkZV9kdXBsaWNhdGVzIDwtIGR1cGxpY2F0ZWQodHhfc3RhdGV3aWRlX2luZm8pCgpwcmludChzdW0obW9iaWxpdHlfZHVwbGljYXRlcywgbmEucm0gPSBUUlVFKSkKcHJpbnQoc3VtKGNlbnN1c19kdXBsaWNhdGVzLCBuYS5ybSA9IFRSVUUpKQpwcmludChzdW0odHhfY291bnR5X2R1cGxpY2F0ZXMsIG5hLnJtID0gVFJVRSkpCnByaW50KHN1bSh0eF9zdGF0ZXdpZGVfZHVwbGljYXRlcywgbmEucm0gPSBUUlVFKSkKCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZXJlIHdlcmUgbm8gZHVwbGljYXRlIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YS4gIEF0IGxlYXN0IG5vbmUgb24gdGhlIGJhc2lzIHRoYXQgdGhlIGRhdGEgd2FzIHRoZSBleGFjdCBzYW1lLgoKYGBge3J9CnVuaXF1ZShtb2JpbGl0eV9kdXBsaWNhdGVzKQpgYGAKCiMjIyBDcmVhdGUgY29sdW1ucyBiYXNlZCBvbiBwb3B1bGF0aW9uCgpgYGB7cn0KIyBtYWtlIGEgcGN0IGluZmVjdGVkIGNvbHVtbiAodmlydXMgc28gc2hvdWxkIGdldCAxIHRpbWUsIGJ1dCB0aGlzIGlzIGRpc3B1dGVkIGZvciBjb3ZpZCkKc3Vic2V0X2NlbnN1c1sncGN0X2luZmVjdGVkJ10gPC0gc3Vic2V0X2NlbnN1c1snY29uZmlybWVkX2Nhc2VzJ10vc3Vic2V0X2NlbnN1c1sndG90YWxfcG9wJ10Kc3Vic2V0X2NlbnN1c1sncGN0X2RlYXRocyddIDwtIHN1YnNldF9jZW5zdXNbJ2RlYXRocyddL3N1YnNldF9jZW5zdXNbJ3RvdGFsX3BvcCddCmhlYWQoc3Vic2V0X2NlbnN1cykKYGBgCgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCnBsb3RfdnNfY291bnR5IDwtIGZ1bmN0aW9uKGRmLCBjb2xfdmFsLCBwZXJjZW50aWxlPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBmaXBzX3RpdGxlPSJjb3VudHlfZmlwc19jb2RlIiwgYmFua3M9NiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF90aXRsZT0iIiwgZ3JhcGhpY190aXRsZT0iIil7CiAgIyBTdWJzZXQgZm9yIHNwZWVkIAogIGRmIDwtIGRmW2MoZmlwc190aXRsZSwgY29sX3ZhbCldCiAgCiAgIyBHZXQgY291bnR5IGRhdGEKICBnY291bnR5IDwtIGdncGxvdDI6Om1hcF9kYXRhKCJjb3VudHkiKQogICMgVVNBIG1hcCBkYXRhCiAgZ3VzYSA8LSBtYXBfZGF0YSgic3RhdGUiKQogIAogIGlmIChiYW5rcyA+IDkpewogICAgbXljb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDksICJSZWRzIikpKGJhbmtzKQogIH0KICAKICAjIEZvcm1hdCB3aXRoIHN1YnJlZ2lvbnMKICBmaXBzdGFiIDwtCiAgICAgIHRyYW5zbXV0ZShtYXBzOjpjb3VudHkuZmlwcywgZmlwcywgY291bnR5ID0gc3ViKCI6LioiLCAiIiwgcG9seW5hbWUpKSAlPiUKICAgICAgdW5pcXVlKCkgJT4lCiAgICAgIHNlcGFyYXRlKGNvdW50eSwgYygicmVnaW9uIiwgInN1YnJlZ2lvbiIpLCBzZXAgPSAiLCIpCiAgCiAgIyBDb21iaW5lIGluIGRlc2lyZWQgb3JkZXIgKE5BIGZvciBtaXNzaW5nKQogIGdjb3VudHkgPC0gbGVmdF9qb2luKGdjb3VudHksIGZpcHN0YWIsIGMoInJlZ2lvbiIsICJzdWJyZWdpb24iKSkKCgogIGRpcyA8LSBkZgogIGRpcyRycHJvcCA8LSByYW5rKGRmW2NvbF92YWxdKQogIGRpcyRwY2xzIDwtIGN1dCgxMDAgKiBwZXJjZW50X3JhbmsoZGZbY29sX3ZhbF0pLCBzZXEoMCwgMTAwLCBsZW4gPSBiYW5rcyksCiAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkKCiAgIyBNaXNzaW5nIGRhdGEKICBhbnRpX2pvaW4oZ2NvdW50eSwgZGlzLCBieSA9IGMoImZpcHMiID0gZmlwc190aXRsZSkpICU+JQogICAgc2VsZWN0KHJlZ2lvbiwgc3VicmVnaW9uKSAlPiUKICAgIHVuaXF1ZSgpCiAgZ2NvdW50eV9wb3AgPC0gbGVmdF9qb2luKGdjb3VudHksIGRpcywgYnkgPSBjKCJmaXBzIiA9IGZpcHNfdGl0bGUpKQogIGZpbGxfdmFscyA8LSBnY291bnR5X3BvcFtjb2xfdmFsXQoKICAjIFBsb3QKICBpZiAobGVnZW5kX3RpdGxlID09ICIiKXsKICAgIGxlZ2VuZF90aXRsZSA8LSBjb2xfdmFsCiAgfQoKICBpZiAocGVyY2VudGlsZSA9PSBGQUxTRSl7CiAgICAjIG5hbWVzKGdjb3VudHlfcG9wKVtuYW1lcyhnY291bnR5X3BvcCkgPT0gY29sX3ZhbF0gPC0gImNvbF9vZl9pbnRlcmVzdCIKICAgIHBsdCA8LSBnZ3Bsb3QoZ2NvdW50eV9wb3ApICsKICAgICAgZ2VvbV9wb2x5Z29uKGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBnZXQoY29sX3ZhbCkpLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleSIsIHNpemUgPSAwLjEsIG5hbWU9IlBlcmNlbnQgSW5mZWN0ZWQiKSArCiAgICAgIGdlb21fcG9seWdvbihhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwKSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwgZGF0YSA9IGd1c2EsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsKICAgICAgY29vcmRfbWFwKCJib25uZSIsIHBhcmFtZXRlcnMgPSA0MS42KSArIGdndGhlbWVzOjp0aGVtZV9tYXAoKSsKICAgICAgc2NhbGVfZmlsbF9ncmFkaWVudDIoKQogICAgICAgIyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIiwgbmEudmFsdWUgPSAiZ3JleSIpCiAgICAgICMgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IHRlcnJhaW4uY29sb3JzKDEwKSkKICB9CgogIGlmIChwZXJjZW50aWxlID09IFRSVUUpewogICAgcGx0IDwtIGdncGxvdChnY291bnR5X3BvcCkgKwogICAgICBnZW9tX3BvbHlnb24oYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IHBjbHMpLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleSIsIHNpemUgPSAwLjEpICsKICAgICAgZ2VvbV9wb2x5Z29uKGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXApLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCBkYXRhID0gZ3VzYSwgY29sb3IgPSAibGlnaHRncmV5IikgKwogICAgICBjb29yZF9tYXAoImJvbm5lIiwgcGFyYW1ldGVycyA9IDQxLjYpICsgZ2d0aGVtZXM6OnRoZW1lX21hcCgpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbXljb2xvcnMsIG5hLnZhbHVlID0gImdyZXkiKSArCiAgICAgICMgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJ2aXJpZGlzIiwgbmEudmFsdWUgPSAiZ3JleSIpICsKICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9ICgxNSkpLAogICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpLCAKICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImxlZnQiKQogIH0KICBwbHQgPC0gcGx0ICsgbGFicyhmaWxsPWxlZ2VuZF90aXRsZSkgKyBnZ3RpdGxlKGdyYXBoaWNfdGl0bGUpCiAgcGx0Cn0KYGBgCgoKYGBge3J9CnBsb3RfdnNfY291bnR5KHN1YnNldF9jZW5zdXMsICJwY3RfaW5mZWN0ZWQiLCBsZWdlbmRfdGl0bGUgPSAiUGVyY2VudCBJbmZlY3RlZCIpCnBsb3RfdnNfY291bnR5KHN1YnNldF9jZW5zdXMsICJwY3RfaW5mZWN0ZWQiLCBwZXJjZW50aWxlID0gVFJVRSwgYmFua3M9MTEsIAogICAgICAgICAgICAgICBsZWdlbmRfdGl0bGUgPSAiUGVyY2VudGlsZSBJbmZlY3RlZCIsCiAgICAgICAgICAgICAgIGdyYXBoaWNfdGl0bGUgPSAiUGVyY2VudGlsZSBvZiBQZXJjZW50YWdlIG9mIFBlb3BsZSBJbmZlY3RlZCBieSBDb3VudHkiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KG1hcHMpCgpjZW5zdXNfaW5mZWN0ZWQgPC0gc3Vic2V0X2NlbnN1c1tjKCJjb3VudHlfZmlwc19jb2RlIiwgInBjdF9pbmZlY3RlZCIpXQojIGNlbnN1c19pbmZlY3RlZCRjb2xvcl9kZW5zaXR5IDwtIHJhaW5ib3cobiA9IDUwLCBjZW5zdXNfaW5mZWN0ZWQkcGN0X2luZmVjdGVkIC8gbWF4KGNlbnN1c19pbmZlY3RlZCRwY3RfaW5mZWN0ZWQpKQpjZW5zdXNfaW5mZWN0ZWQkY29sb3JfZGVuc2l0eSA8LSBoZWF0LmNvbG9ycyg1MCwgY2Vuc3VzX2luZmVjdGVkJHBjdF9pbmZlY3RlZC9tYXgoY2Vuc3VzX2luZmVjdGVkJHBjdF9pbmZlY3RlZCkpCmhlYWQoY2Vuc3VzX2luZmVjdGVkKQojIGNvdW50eV9pbmZlY3RlZF9kYXRhIDwtIAojIG1hcCgiY291bnR5IiwgZmlsbD1UUlVFLCBjb2w9Y291bnRpZXMkY29sb3IpCmBgYAoKYGBge3J9CmRhdGEoY291bnR5LmZpcHMpCiMjIFNldCB1cCBmYWtlIGRmX3BvcF9jb3VudHkgZGF0YSBmcmFtZQpkZl9wb3BfY291bnR5IDwtIGRhdGEuZnJhbWUocmVnaW9uPWNvdW50eS5maXBzJGZpcHMpCmRmX3BvcF9jb3VudHkkdmFsdWUgPC0gY291bnR5LmZpcHMkZmlwcwp5IDwtIGRmX3BvcF9jb3VudHkkdmFsdWUKZGZfcG9wX2NvdW50eSRjb2xvciA8LSBncmF5KHkgLyBtYXgoeSkpCgojIyBtZXJnZSBwb3B1bGF0aW9uIGRhdGEgd2l0aCBjb3VudHkuZmlwcyB0byBtYWtlIHN1cmUgY29sb3IgY29sdW1uIGlzCiMjIG9yZGVyZWQgY29ycmVjdGx5Lgpjb3VudGllcyA8LSBjb3VudHkuZmlwcyAlPiUgbGVmdF9qb2luKGRmX3BvcF9jb3VudHksIGJ5PWMoJ2ZpcHMnPSdyZWdpb24nKSkKIyBwcmludChoZWFkKGNvdW50aWVzKSkKCm5ld2RhdGEgPC0gY2Vuc3VzX2luZmVjdGVkW29yZGVyKGNlbnN1c19pbmZlY3RlZCRjb3VudHlfZmlwc19jb2RlKSxdCm5ld2RhdGEKCiMgVGhyb3VnaCBhd2F5IGNvdW50aWVzIHRoYXQgYXJlIG5vdCBpbiBkZWZhdWx0Cm5ld19kYXRhIDwtIG5ld2RhdGFbKG5ld2RhdGEkY291bnR5X2ZpcHNfY29kZSAlaW4lIGNvdW50aWVzJGZpcHMpLF0KbmV3X2RhdGEKYGBgCgoKYGBge3J9CmRhdGEoY291bnR5LmZpcHMpCiMjIFNldCB1cCBmYWtlIGRmX3BvcF9jb3VudHkgZGF0YSBmcmFtZQpkZl9wb3BfY291bnR5IDwtIGRhdGEuZnJhbWUocmVnaW9uPWNvdW50eS5maXBzJGZpcHMpCmRmX3BvcF9jb3VudHkkdmFsdWUgPC0gY291bnR5LmZpcHMkZmlwcwp5IDwtIGRmX3BvcF9jb3VudHkkdmFsdWUKZGZfcG9wX2NvdW50eSRjb2xvciA8LSBncmF5KHkgLyBtYXgoeSkpCgojIyBtZXJnZSBwb3B1bGF0aW9uIGRhdGEgd2l0aCBjb3VudHkuZmlwcyB0byBtYWtlIHN1cmUgY29sb3IgY29sdW1uIGlzCiMjIG9yZGVyZWQgY29ycmVjdGx5Lgpjb3VudGllcyA8LSBjb3VudHkuZmlwcyAlPiUgbGVmdF9qb2luKGRmX3BvcF9jb3VudHksIGJ5PWMoJ2ZpcHMnPSdyZWdpb24nKSkKbWFwKCJjb3VudHkiLCBmaWxsPVRSVUUsIGNvbD1jb3VudGllcyRjb2xvcikKYGBgCmBgYHtyfQpjZW5zdXNfaW5mZWN0ZWQKYGBgCgpgYGB7cn0KZGF0YShjb3VudHkuZmlwcykKZGZfcG9wX2NvdW50eSA8LSBkYXRhLmZyYW1lKHJlZ2lvbj1jb3VudHkuZmlwcyRmaXBzKQpkZl9wb3BfY291bnR5CmBgYAoKYGBge3J9CmNvdW50aWVzIDwtIGNvdW50eS5maXBzICU+JSBsZWZ0X2pvaW4oY2Vuc3VzX2luZmVjdGVkLCBieT1jKCdmaXBzJz0nY291bnR5X2ZpcHNfY29kZScpKQpjb3VudGllcwpgYGAKCgoKCmBgYHtyfQpnZ3Bsb3QyOjptYXBfZGF0YSgiY291bnR5IikKYXNfdGliYmxlKG1hcHM6OmNvdW50eS5maXBzKQojIGRhbGwgPC0gZ2dwbG90Mjo6bWFwX2RhdGEoImNvdW50eSIpICU+JSBsZWZ0X2pvaW4oYXNfdGliYmxlKG1hcHM6OmNvdW50eS5maXBzKSkKYGBgCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJtYXBwcm9qIikKYGBgCgpgYGB7cn0KIyBodHRwOi8vaG9tZXBhZ2Uuc3RhdC51aW93YS5lZHUvfmx1a2UvY2xhc3Nlcy9TVEFUNDU4MC0yMDIwL21hcHMuaHRtbAojIENob3JvcGxldGggTWFwcyBvZiBDb3VudHkgUG9wdWxhdGlvbgpsaWJyYXJ5KGdncGxvdDIpCiMgaW5zdGFsbC5wYWNrYWdlcyhtYXBwcm9qKQpsaWJyYXJ5KG1hcHByb2opCgpnY291bnR5IDwtIGdncGxvdDI6Om1hcF9kYXRhKCJjb3VudHkiKQpnZ3Bsb3QoZ2NvdW50eSkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXApLAogICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4wNSkgKwogICAgY29vcmRfbWFwKCJib25uZSIsIHBhcmFtZXRlcnMgPSA0MS42KQpgYGAKCgpgYGB7cn0KaGVhZChmaWx0ZXIobWFwczo6Y291bnR5LmZpcHMsIGdyZXBsKCI6IiwgcG9seW5hbWUpKSkKZmlwc3RhYiA8LQogICAgdHJhbnNtdXRlKG1hcHM6OmNvdW50eS5maXBzLCBmaXBzLCBjb3VudHkgPSBzdWIoIjouKiIsICIiLCBwb2x5bmFtZSkpICU+JQogICAgdW5pcXVlKCkgJT4lCiAgICBzZXBhcmF0ZShjb3VudHksIGMoInJlZ2lvbiIsICJzdWJyZWdpb24iKSwgc2VwID0gIiwiKQpoZWFkKGZpcHN0YWIpCmdjb3VudHkgPC0gbGVmdF9qb2luKGdjb3VudHksIGZpcHN0YWIsIGMoInJlZ2lvbiIsICJzdWJyZWdpb24iKSkKaGVhZChnY291bnR5KQpgYGAKYGBge3J9CnRlc3QgPC0gZ2NvdW50eSAlPiUgbGVmdF9qb2luKGNlbnN1c19pbmZlY3RlZCwgYnk9YygnZmlwcyc9J2NvdW50eV9maXBzX2NvZGUnKSkKdGVzdApgYGAKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikKbGlicmFyeShnZ3RoZW1lcykKYGBgCmBgYHtyfQpjZW5zdXNfaW5mZWN0ZWQKYGBgCmBgYHtyfQpjZW5zdXNfaW5mZWN0ZWQgJT4lIHJhbmsocGN0X2luZmVjdGVkKQpgYGAKCgpgYGB7cn0KbmNscyA8LSA2CncgPC0gY2Vuc3VzX2luZmVjdGVkICU+JSBzZWxlY3QoZmlwcyA9IGNvdW50eV9maXBzX2NvZGUsIHBjdF9pbmZlY3RlZCkgJT4lIG11dGF0ZShycG9wID0gcmFuayhwY3RfaW5mZWN0ZWQpLAogICAgICAgICAgIHBjbHMgPSBjdXQoMTAwICogcGVyY2VudF9yYW5rKHBjdF9pbmZlY3RlZCksIHNlcSgwLCAxMDAsIGxlbiA9IG5jbHMpLAogICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkKdwpgYGAKYGBge3J9CmFudGlfam9pbihnY291bnR5LCB3LCAiZmlwcyIpICU+JQogICAgc2VsZWN0KHJlZ2lvbiwgc3VicmVnaW9uKSAlPiUKICAgIHVuaXF1ZSgpCgpnY291bnR5X3BvcCA8LSBsZWZ0X2pvaW4oZ2NvdW50eSwgdywgImZpcHMiKQojIGZpbHRlcihnY291bnR5X3BvcCwgaXMubmEocnBvcCkpICU+JQojICAgICBzZWxlY3QocmVnaW9uLCBzdWJyZWdpb24sIHBvcCwgcnBvcCwgcGNscykgJT4lCiMgICAgIHVuaXF1ZSgpCmBgYApgYGB7cn0KZ2NvdW50eV9wb3AKYGBgCgpgYGB7cn0KZ3VzYSA8LSBtYXBfZGF0YSgic3RhdGUiKQpnZ3Bsb3QoZ2NvdW50eV9wb3ApICsKICAgIGdlb21fcG9seWdvbihhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gcGN0X2luZmVjdGVkKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmV5Iiwgc2l6ZSA9IDAuMSwgbmFtZT0iUGVyY2VudCBJbmZlY3RlZCIpICsKICAgIGdlb21fcG9seWdvbihhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwKSwKICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIGRhdGEgPSBndXNhLCBjb2xvciA9ICJsaWdodGdyZXkiKSArCiAgICBjb29yZF9tYXAoImJvbm5lIiwgcGFyYW1ldGVycyA9IDQxLjYpICsgZ2d0aGVtZXM6OnRoZW1lX21hcCgpCmBgYAoKYGBge3J9CmdncGxvdChnY291bnR5X3BvcCkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBwY2xzKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmV5Iiwgc2l6ZSA9IDAuMSkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXApLAogICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwgZGF0YSA9IGd1c2EsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsKICAgIGNvb3JkX21hcCgiYm9ubmUiLCBwYXJhbWV0ZXJzID0gNDEuNikgKyBnZ3RoZW1lczo6dGhlbWVfbWFwKCkgKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJSZWRzIiwgbmEudmFsdWUgPSAiYmx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlBlcmNlbnRpbGUiKSArCiAgICB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpKQpgYGAKYGBge3J9CnBsb3RfdnNfY291bnR5IDwtIGZ1bmN0aW9uKGRmLCBjb2xfdmFsLCBwZXJjZW50aWxlPUZBTFNFLCBmaXBzX3RpdGxlPSJjb3VudHlfZmlwc19jb2RlIiwgYmFua3M9NiwgbGVnZW5kX3RpdGxlPSIiKXsKICAjIFN1YnNldCBmb3Igc3BlZWQgCiAgZGYgPC0gZGZbYyhmaXBzX3RpdGxlLCBjb2xfdmFsKV0KICAKICAjIEdldCBjb3VudHkgZGF0YQogIGdjb3VudHkgPC0gZ2dwbG90Mjo6bWFwX2RhdGEoImNvdW50eSIpCiAgIyBVU0EgbWFwIGRhdGEKICBndXNhIDwtIG1hcF9kYXRhKCJzdGF0ZSIpCiAgCiAgIyBGb3JtYXQgd2l0aCBzdWJyZWdpb25zCiAgZmlwc3RhYiA8LQogICAgICB0cmFuc211dGUobWFwczo6Y291bnR5LmZpcHMsIGZpcHMsIGNvdW50eSA9IHN1YigiOi4qIiwgIiIsIHBvbHluYW1lKSkgJT4lCiAgICAgIHVuaXF1ZSgpICU+JQogICAgICBzZXBhcmF0ZShjb3VudHksIGMoInJlZ2lvbiIsICJzdWJyZWdpb24iKSwgc2VwID0gIiwiKQogIAogICMgQ29tYmluZSBpbiBkZXNpcmVkIG9yZGVyIChOQSBmb3IgbWlzc2luZykKICBnY291bnR5IDwtIGxlZnRfam9pbihnY291bnR5LCBmaXBzdGFiLCBjKCJyZWdpb24iLCAic3VicmVnaW9uIikpCgoKICBkaXMgPC0gZGYKICBkaXMkcnByb3AgPC0gcmFuayhkZltjb2xfdmFsXSkKICBkaXMkcGNscyA8LSBjdXQoMTAwICogcGVyY2VudF9yYW5rKGRmW2NvbF92YWxdKSwgc2VxKDAsIDEwMCwgbGVuID0gYmFua3MpLAogICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpCgogICMgTWlzc2luZyBkYXRhCiAgYW50aV9qb2luKGdjb3VudHksIGRpcywgYnkgPSBjKCJmaXBzIiA9IGZpcHNfdGl0bGUpKSAlPiUKICAgIHNlbGVjdChyZWdpb24sIHN1YnJlZ2lvbikgJT4lCiAgICB1bmlxdWUoKQogIGdjb3VudHlfcG9wIDwtIGxlZnRfam9pbihnY291bnR5LCBkaXMsIGJ5ID0gYygiZmlwcyIgPSBmaXBzX3RpdGxlKSkKICBmaWxsX3ZhbHMgPC0gZ2NvdW50eV9wb3BbY29sX3ZhbF0KCiAgIyBQbG90CiAgaWYgKGxlZ2VuZF90aXRsZSA9PSAiIil7CiAgICBsZWdlbmRfdGl0bGUgPC0gY29sX3ZhbAogIH0KCiAgaWYgKHBlcmNlbnRpbGUgPT0gRkFMU0UpewogICAgIyBuYW1lcyhnY291bnR5X3BvcClbbmFtZXMoZ2NvdW50eV9wb3ApID09IGNvbF92YWxdIDwtICJjb2xfb2ZfaW50ZXJlc3QiCiAgICBwbHQgPC0gZ2dwbG90KGdjb3VudHlfcG9wKSArCiAgICAgIGdlb21fcG9seWdvbihhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gZ2V0KGNvbF92YWwpKSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiLCBzaXplID0gMC4xLCBuYW1lPSJQZXJjZW50IEluZmVjdGVkIikgKwogICAgICBnZW9tX3BvbHlnb24oYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCksCiAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIGRhdGEgPSBndXNhLCBjb2xvciA9ICJsaWdodGdyZXkiKSArCiAgICAgIGNvb3JkX21hcCgiYm9ubmUiLCBwYXJhbWV0ZXJzID0gNDEuNikgKyBnZ3RoZW1lczo6dGhlbWVfbWFwKCkKICB9CgogIGlmIChwZXJjZW50aWxlID09IFRSVUUpewogICAgcGx0IDwtIGdncGxvdChnY291bnR5X3BvcCkgKwogICAgICBnZW9tX3BvbHlnb24oYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IHBjbHMpLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleSIsIHNpemUgPSAwLjEpICsKICAgICAgZ2VvbV9wb2x5Z29uKGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXApLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCBkYXRhID0gZ3VzYSwgY29sb3IgPSAibGlnaHRncmV5IikgKwogICAgICBjb29yZF9tYXAoImJvbm5lIiwgcGFyYW1ldGVycyA9IDQxLjYpICsgZ2d0aGVtZXM6OnRoZW1lX21hcCgpICsKICAgICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJSZWRzIiwgbmEudmFsdWUgPSAiYmx1ZSIpICsKICAgICAgdGhlbWUobGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BKSkKICB9CiAgcGx0IDwtIHBsdCArIGxhYnMoZmlsbD1sZWdlbmRfdGl0bGUpCiAgcGx0Cn0KYGBgCgpgYGB7cn0Kc3Vic2V0X2NlbnN1cwojIHBsb3RfdnNfY291bnR5KHN1YnNldF9jZW5zdXMsICJwY3RfaW5mZWN0ZWQiLCBsZWdlbmRfdGl0bGUgPSAiUGVjZW50IEluZmVjdGVkIikKYGBgCmBgYHtyfQpwbG90X3ZzX2NvdW50eShzdWJzZXRfY2Vuc3VzLCAicGN0X2luZmVjdGVkIiwgcGVyY2VudGlsZSA9IFRSVUUsIGxlZ2VuZF90aXRsZSA9ICJQZXJjZW50aWxlIEluZmVjdGVkIiwgYmFua3MgPSAxMCkKYGBgCgoKCg==